namespace nwWCF
{	
	using System.Collections;
    using System.Collections.Generic;
    using System.Globalization;
    using System.Linq;
    using System.Linq.Dynamic;
    using System.Reflection;
    using System.Text;
    using Telerik.OpenAccess;

    /// <summary>
    /// Data Manager Class Handler 
    /// </summary>
    public partial class DataManager
    {
        #region Private Memebrs

        /// <summary>
        /// Object scope 
        /// </summary>
        private IObjectScope scope = null;

        #endregion

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="DataManager"/> class.
        /// </summary>
        public DataManager()
        {
            InitializeManager();
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="DataManager"/> class.
        /// </summary>
        /// <param name="scope">Object scope <see cref="IObjectScope"/>.</param>
        public DataManager(IObjectScope scope)
        {
            this.scope = scope;
            InitializeManager();
        }

        #endregion

        #region IDisposable Members

        /// <summary>
        /// Dispose object scope.
        /// </summary>
        public void Dispose()
        {
            if (this.scope != null)
            {
                this.scope.Dispose();
                this.scope = null;
            }
        }

        #endregion

        #region Public Functions

		/// <summary>
        /// Gets the entity id.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="entity">The entity.</param>
        /// <returns>entity id</returns>
        public string GetEntityId<T>(T entity)
        {
            IObjectId id = Database.OID.GetObjectId(entity);
            return id.ToString();
        }
        
        /// <summary>
        /// Create entity.
        /// </summary>
        /// <typeparam name="T">Open Access class type. </typeparam>
        /// <param name="entity">Open Access entity.</param>
        /// <returns>Object Id that was generated by OpenAccess.</returns>
        public string CreateEntity<T>(T entity)
        {
            try
            {
                this.scope.Add(entity);
                IObjectId entityId = Database.GetObjectId(entity);
				this.scope.Transaction.Commit();
                return entityId.ToString();
            }
            catch (Telerik.OpenAccess.OpenAccessException ex)
            {
                this.RollBackTransaction(this.scope);
                throw ex;
            }
        }

        /// <summary>
        /// Read All Entities.
        /// </summary>
        /// <typeparam name="T">Open Access entity type.</typeparam>
        /// <param name="startIndex">Start Index.</param>
        /// <param name="count">Entities Count.</param>
        /// <returns>All entities list of class type.</returns>
        public List<T> ReadEntities<T>(int startIndex, int count)
        {
            try
            {
                var returnedList = this.scope.Extent<T>().Skip(startIndex).Take(count);
                return returnedList.ToList();
            }
            catch (Telerik.OpenAccess.OpenAccessException ex)
            {
                this.RollBackTransaction(this.scope);
                throw ex;
            }
        }

        /// <summary>
        /// Read All Entities.
        /// </summary>
        /// <typeparam name="T">Open Access entity type.</typeparam>
        /// <returns>All entities list of class type.</returns>
        public List<T> ReadEntities<T>()
        {
            try
            {
                var returnedList = this.scope.Extent<T>();
                return returnedList.ToList();
            }
            catch (Telerik.OpenAccess.OpenAccessException ex)
            {
                this.RollBackTransaction(this.scope);
                throw ex;
            }
        }

        /// <summary>
        /// Get Max number of entity
        /// </summary>
        /// <typeparam name="T">Entity Type.</typeparam>
        /// <returns>entities Count Number.</returns>
        public int GetMaxEntitiesCount<T>()
        {
            try
            {
                var returnedListcount = this.scope.Extent<T>().Count();
                return returnedListcount; 

            }
            catch (Telerik.OpenAccess.OpenAccessException ex)
            {
                this.RollBackTransaction(this.scope);
                throw ex;
            }
        }

        /// <summary>
        /// Read Entity.
        /// </summary>
        /// <typeparam name="T">Open Access entity type.</typeparam>
        /// <param name="entityId">Target entity Id.</param>
        /// <returns>Target entity.</returns>
        public T ReadEntity<T>(string entityId)
        {
            try
            {
                IObjectId exsitingObjectId = Database.OID.ParseObjectId(typeof(T), entityId);
                object entity = this.scope.GetObjectById(exsitingObjectId);
                return (T)entity;
            }
            catch (Telerik.OpenAccess.OpenAccessException ex)
            {
                this.RollBackTransaction(this.scope);
                throw ex;
            }
        }

        /// <summary>
        /// Update entity.
        /// </summary>
        /// <typeparam name="T">Open Access entity type.</typeparam>
        /// <param name="entity">Updated entity.</param>
        /// <returns>Updated entity Id.</returns>
        public string UpdateEntity<T>(T entity)
        {
            try
            {
				string entityId = this.GetEntityId<T>(entity);
                T existingObject = this.ReadEntity<T>(entityId);
                this.ExecuteCopy(entity, existingObject);
				this.scope.Transaction.Commit();
                return entityId;
            }
            catch (Telerik.OpenAccess.OpenAccessException ex)
            {
                this.RollBackTransaction(this.scope);
                throw ex;
            }
        }

        /// <summary>
        /// Delete Entity.
        /// </summary>
        /// <typeparam name="T">Open Access entity type.</typeparam>
        /// <param name="entityId">Target entity Id.</param>
        /// <returns>Deleted entity Id.</returns>
        public string DeleteEntity<T>(string entityId)
        {
            try
            {
                IObjectId objectId = Database.OID.ParseObjectId(typeof(T), entityId);
                object entity = this.scope.GetObjectById(objectId);
                this.scope.Remove(entity);
				this.scope.Transaction.Commit();
                return entityId.ToString();
            }
            catch (Telerik.OpenAccess.OpenAccessException ex)
            {
                this.RollBackTransaction(this.scope);
                throw ex;
            }
        }
		
		/// <summary>
        /// Read All Entities.
        /// </summary>
        /// <typeparam name="T">Open Access entity type.</typeparam>
        /// <param name="searchParameters">Object containing search parameters. If a property's value should be ignored, then it should be set to the default value.</param>
        /// <returns>All entities list of class type.</returns>
        public List<T> SearchEntities<T>(T searchParameters)
        {
            var parameters = new List<KeyValuePair<string, object>>();

            foreach (var item in searchParameters.GetType().GetProperties(
                BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public))
            {
                try
                {
                    // Get the stringvalue attributes  
                    var attributes = item.GetCustomAttributes(
                        typeof(FieldAliasAttribute), false);

                    if (attributes.Count() != 0)
                    {
                        var value = item.GetValue(
                            searchParameters,
                            BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public,
                            null,
                            null,
                            CultureInfo.InvariantCulture);

                        // Makes sure the property is not the default value.  
                        // ! Cannot execute Equals(value, default((Type)variable)) - this does not compile! Using null converts this to 0 for value-types, so it works.  
                        if (!Equals(value, null))
                        {
                            parameters.Add(new KeyValuePair<string, object>(
                                item.Name,
                                value));
                        }
                    }
                }
                catch
                {
                    // Not sure what would happen here.  
                    continue;
                }
            }

            var paramString = new StringBuilder();
            var paramValues = new ArrayList();
            for (var i = 0; i < parameters.Count; i++)
            {
                if (i > 0)
                {
                    paramString.Append(" and ");
                }

                paramString.Append(parameters[i].Key);
                paramString.Append(" = @");
                paramString.Append(i.ToString());

                paramValues.Add(parameters[i].Value);
            }

            try
            {
                var results = scope.Extent<T>().Where(
                    paramString.ToString(), 
                    paramValues.ToArray());

                return results.ToList();
            }
            catch (OpenAccessException ex)
            {
                RollBackTransaction(scope);
                throw ex;
            }
        }

        #endregion
        
        #region Helper Functions

        /// <summary>
        /// Initializes the manager.
        /// </summary>
        private void InitializeManager()
        {
            nwDAL.ObjectScopeProvider1.AdjustForDynamicLoad();
            if (scope == null)
            {
                this.scope = nwDAL.ObjectScopeProvider1.GetNewObjectScope();
                nwDAL.ObjectScopeProvider1.AdjustForDynamicLoad();
            }

            this.scope.TransactionProperties.AutomaticBegin = true;
        }
        
		/// <summary>
        /// Copy helper function to execute copy
        /// </summary>
        /// <param name="sourceObject">the source object that will be copied</param>
        /// <param name="destinationObject">the destination object that will be copies to.</param>
        private void ExecuteCopy(object sourceObject, object destinationObject)
        {
            foreach (var item in sourceObject.GetType().GetProperties(
                System.Reflection.BindingFlags.Public |
                System.Reflection.BindingFlags.Instance |
                System.Reflection.BindingFlags.Static))
            {
                try
                {
                    // Get the stringvalue attributes
                    var attributes = item.GetCustomAttributes(
                         typeof(Telerik.OpenAccess.FieldAliasAttribute), false);

                    if (attributes.Count() != 0)
                    {
                        sourceObject.GetType().GetProperty(
                            item.Name,
                            System.Reflection.BindingFlags.Public |
                            System.Reflection.BindingFlags.Instance |
                            System.Reflection.BindingFlags.Static).SetValue(
                                destinationObject,
                                item.GetValue(
                                    sourceObject,
                                    System.Reflection.BindingFlags.Public |
                                    System.Reflection.BindingFlags.Static |
                                    System.Reflection.BindingFlags.Instance,
                                    null,
                                    null,
                                System.Globalization.CultureInfo.InvariantCulture),
                                System.Reflection.BindingFlags.Public |
                                System.Reflection.BindingFlags.Instance |
                                System.Reflection.BindingFlags.Static,
                                null,
                                null,
                                System.Globalization.CultureInfo.InvariantCulture);
                    }
                }
                catch
                {
                    continue;
                }
            }
        }
		
        /// <summary>
        /// Roll Back the trancation 
        /// </summary>
        /// <param name="scope"> The scope of the trancation </param>
        private void RollBackTransaction(IObjectScope scope)
        {
            try
            {
                if (this.scope != null)
                {
                    this.scope.Transaction.Rollback();
                }
            }
            catch
            {
                return;
            }
        }
		
        #endregion
    }
}
